package top.hmtools.base;

import java.math.BigInteger;

import top.hmtools.system.SystemInfoTools;

/**
 * 猜测字符编码工具
 * @author HyboJ
 *
 */
public class CharsetGuessTools {

	/**
	 * 根据字节码数组，猜测编码类型
	 * <br>依据：如果文本头有bom信息则可知其对应的字符编码。
	 *  <br>此外，GBK与UTF-8都有可能，二者都可以没有bom信息。经观察发现，ASCII对应的字符是二者一致的部分，
	 *  <br>经尝试多个中文字符，发现在UTF-8中的编码在以16进制字符串表示时，基本上是以“E”开头，而GBK则不是。
	 * <br>准确性还有待验证，如果猜测不准确，请联系作者：<a href="http://www.hybomyth.com">嗨啵</a>
	 * <br>电子邮箱：J@hybo.net
	 * @param bytes
	 * @return
	 */
	public static String doGuess(byte[] bytes){
		if(bytes ==null || bytes.length<1){
			return null;
		}
		//如果含有bom头信息的话
		String string = binary(bytes);
		ECharsetBom[] bomHeads = ECharsetBom.values();
        for(ECharsetBom bom:bomHeads){
            if(string.toUpperCase().startsWith(bom.getBomHead().toUpperCase())){
                return bom.getEncodeName();
            }
        }
        
        //没有bom头信息的话
        for(byte bt:bytes){
        	String hex = byteToHex(bt);
        	//忽略ASCII码部分
        	int btInt = Integer.parseInt(hex, 16);
        	if(btInt<128&&btInt>=0){
        		continue;
        	}
        	if(hex.toUpperCase().startsWith("E")){
        		return "UTF-8";
        	}else{
        		return "GBK";
        	}
        }
        
//        string=string.toUpperCase();
//        int firstIndex = string.indexOf("E");
//        if(firstIndex>=0 && firstIndex%2==0){
//        	//如果16进制的字符串中，"E"首次出现的下标号是偶数的话，则该文本应该是UTF-8字符编码
//        	return "UTF-8";
//        }else{
//        	//进一步猜测
//        	int indexStart = firstIndex+1;
//        	while(indexStart>0 && indexStart<string.length()){
//        		indexStart = string.indexOf("E", indexStart);
//        		if(indexStart%2==0){
//        			return "UTF-8";
//        		}else{
//        			indexStart++;
//        		}
//        	}
//        }
        if(SystemInfoTools.isLinux()){
        	return "UTF-8";
        }else  if(SystemInfoTools.isWindows()){
        	return "GBK";
        }else{
        	return "GB2312";
        }
	}
	
	private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
	/**
	 * 将单个byte转换为16进制表示的字符串
	 * @param b
	 * @return
	 */
	private static String byteToHex(byte b){
    	char[] buf = new char[2];
    	int a = 0;
        int index = 0;
    	if(b < 0) {
            a = 256 + b;
        } else {
            a = b;
        }

        buf[index++] = HEX_CHAR[a / 16];
        buf[index++] = HEX_CHAR[a % 16];
        return new String(buf);
    }
	
	/**
	 * TODO 该方法极度消耗计算机资源，须进行处理
	 * 将字节码数组转换为指定的进制字符串
	 * @param bytes
	 * @param radix
	 * @return
	 */
//	private static String binary(byte[] bytes, int radix){  
//        return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数  
//    }  
	
	/**
	 * 将字节码数组转换为指定的进制字符串
	 */
	private static String binary(byte[] bytes) {
        // 一个byte为8位，可用两个十六进制位标识
        char[] buf = new char[bytes.length * 2];
        int a = 0;
        int index = 0;
        for(byte b : bytes) { // 使用除与取余进行转换
            if(b < 0) {
                a = 256 + b;
            } else {
                a = b;
            }

            buf[index++] = HEX_CHAR[a / 16];
            buf[index++] = HEX_CHAR[a % 16];
        }

        return new String(buf);
    }
}
